home *** CD-ROM | disk | FTP | other *** search
/ Aminet 34 / Aminet 34 (2000)(Schatztruhe)[!][Dec 1999].iso / Aminet / util / gnu / unixcmds.lha / unixcmds / src / join.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-06  |  10.4 KB  |  377 lines

  1. /* join - relation data base operator   Author:  Saeko Hirabayashi */
  2.  
  3. /* Written by Saeko Hirabayashi, 1989.
  4.  * 1992-01-28 Modified by Kouichi Hirabayashi to add some POSIX1003.2 options.
  5.  *
  6.  * This a free program.
  7.  */
  8.  
  9. #include <string.h>
  10. #include <stdio.h>
  11.  
  12. #define MAXFLD  200             /* maximum # of fields to accept */
  13.  
  14. void main  (int argc, char **argv);
  15. void error  (char *s, char *t);
  16. void usage  (void);
  17. void match  (void);
  18. void f1_only  (void);
  19. void f2_only  (void);
  20. void output  (int flag);
  21. void outfld  (int file);
  22. void outputf  (int flag);
  23. int compare  (void);
  24. int get1  (void);
  25. int get2  (int back);
  26. int getrec  (int file);
  27. int split  (int file);
  28. int atoi  (char *str);
  29. int exit  (int val);
  30. FILE * efopen  (char *file, char *mode);
  31. void (*outfun)  (int file); /* output func: output() or outputf()*/
  32.  
  33. #define F1      1
  34. #define F2      2
  35. #define SEP     (sep ? sep : ' ')
  36.  
  37. FILE *fp[2];                    /* file pointer for file1 and file2 */
  38. long head;                      /* head of the current (same)key group of the
  39.                                  * file2 */
  40.  
  41. char buf[2][BUFSIZ];            /* input buffer for file1 and file2 */
  42. char *fld[2][MAXFLD];           /* field vector for file1 and file2 */
  43. int nfld[2];                    /* # of fields for file1 and file2 */
  44.  
  45. int kpos[2];                    /* key field position for file1 and file2
  46.                                  * (from 0) */
  47. char oldkey[BUFSIZ];            /* previous key of the file1 */
  48.  
  49. struct {                        /* output list by -o option */
  50.   int o_file;                   /* file #: 0 or 1 */
  51.   int o_field;                  /* field #: 0, 1, 2, .. */
  52. } olist[MAXFLD];
  53. int nout;                       /* # of output filed */
  54.  
  55. int aflag;                      /* n for '-an': F1 or F2 or both */
  56. int vflag;                      /* n for '-vn': F1 or F2 or both */
  57. char *es;                       /* s for '-e s' */
  58. char sep;                       /* c for -tc: filed separator */
  59. char *cmd;                      /* name of this program */
  60.  
  61. void main(argc, argv)
  62. /* [<][>][^][v][top][bottom][index][help] */
  63. int argc;
  64. char **argv;
  65. {
  66.   register char *s;
  67.   int c, i, j;
  68.  
  69.   cmd = argv[0];
  70.   outfun = output;              /* default output form */
  71.  
  72.   while (--argc > 0 && (*++argv)[0] == '-' && (*argv)[1]) {
  73.         /* "-" is a file name (stdin) */
  74.         s = argv[0] + 1;
  75.         if ((c = *s) == '-' && !s[1]) {
  76.                 ++argv;
  77.                 --argc;
  78.                 break;          /* -- */
  79.         }
  80.         if (*++s == '\0') {
  81.                 s = *++argv;
  82.                 --argc;
  83.         }
  84.         switch (c) {
  85.             case 'a':           /* add unpairable line to output */
  86.                 vflag = 0;
  87.                 switch (*s) {
  88.                     case '1':   aflag |= F1;    break;
  89.                     case '2':   aflag |= F2;    break;
  90.                     default:    aflag |= (F1 | F2);     break;
  91.                 }
  92.                 break;
  93.  
  94.             case 'e':           /* replace empty field by es */
  95.                 es = s;
  96.                 break;
  97.  
  98.             case 'j':           /* key field (obsolute) */
  99.                 c = *s++;
  100.                 if (*s == '\0') {
  101.                         s = *++argv;
  102.                         --argc;
  103.                 }
  104.  
  105.             case '1':           /* key field of file1 */
  106.             case '2':           /* key field of file2 */
  107.                 i = atoi(s) - 1;
  108.  
  109.                 switch (c) {
  110.                     case '1':   kpos[0] = i;    break;
  111.                     case '2':   kpos[1] = i;    break;
  112.                     default:    kpos[0] = kpos[1] = i;
  113.                                 break;
  114.                 }
  115.                 break;
  116.  
  117.             case 'o':           /* specify output format */
  118.                 do {
  119.                         i = j = 0;
  120.                         sscanf(s, "%d.%d", &i, &j);
  121.                         if (i < 1 || j < 1 || i > 2) usage();
  122.                         olist[nout].o_file = i - 1;
  123.                         olist[nout].o_field = j - 1;
  124.                         nout++;
  125.                         if ((s = strchr(s, ',')) != (char *) 0)
  126.                                 s++;
  127.                         else {
  128.                                 s = *++argv;
  129.                                 --argc;
  130.                         }
  131.                 } while (argc > 2 && *s != '-');
  132.                 ++argc;
  133.                 --argv;         /* compensation */
  134.                 outfun = outputf;
  135.                 break;
  136.  
  137.             case 't':           /* tab char */
  138.                 sep = *s;
  139.                 break;
  140.  
  141.             case 'v':           /* output unpairable line only */
  142.                 aflag = 0;
  143.                 switch (*s) {
  144.                     case '1':   vflag |= F1;    break;
  145.                     case '2':   vflag |= F2;    break;
  146.                     default:    vflag |= (F1 | F2);     break;
  147.                 }
  148.                 break;
  149.  
  150.             default:    usage();
  151.         }
  152.   }
  153.   if (argc != 2) usage();
  154.  
  155.   fp[0] = strcmp(argv[0], "-") ? efopen(argv[0], "r") : stdin;
  156.   fp[1] = efopen(argv[1], "r");
  157.  
  158.   nfld[0] = get1();             /* read file1 */
  159.   nfld[1] = get2(0);            /* read file2 */
  160.  
  161.   while (nfld[0] || nfld[1]) {
  162.         if ((i = compare()) == 0)
  163.                 match();
  164.         else if (i < 0)
  165.                 f1_only();
  166.         else
  167.                 f2_only();
  168.   }
  169.   fflush(stdout);
  170.  
  171.   exit(0);
  172. }
  173.  
  174. void usage()
  175. /* [<][>][^][v][top][bottom][index][help] */
  176. {
  177.   fprintf(stderr,
  178.     "Usage: %s [-an|-vn] [-e str] [-o list] [-tc] [-1 f] [-2 f] file1 file2\n",
  179.     cmd);
  180.   exit(1);
  181. }
  182.  
  183. int compare()
  184. /* [<][>][^][v][top][bottom][index][help] */
  185. {                               /* compare key field */
  186.   register int r;
  187.  
  188.   if (nfld[1] == 0)             /* file2 EOF */
  189.         r = -1;
  190.   else if (nfld[0] == 0)        /* file1 EOF */
  191.         r = 1;
  192.   else {
  193.         if (nfld[0] <= kpos[0])
  194.                 error("missing key field in file1", (char *) 0);
  195.         if (nfld[1] <= kpos[1])
  196.                 error("missing key field in file2", (char *) 0);
  197.  
  198.         r = strcmp(fld[0][kpos[0]], fld[1][kpos[1]]);
  199.   }
  200.   return r;
  201. }
  202.  
  203. void match()
  204. /* [<][>][^][v][top][bottom][index][help] */
  205. {
  206.   long p;
  207.  
  208.   if (!vflag) (*outfun) (F1 | F2);
  209.  
  210.   p = ftell(fp[1]);
  211.   nfld[1] = get2(0);            /* check key order */
  212.   if (nfld[1] == 0 || strcmp(fld[0][kpos[0]], fld[1][kpos[1]])) {
  213.         nfld[0] = get1();
  214.         if (strcmp(fld[0][kpos[0]], oldkey) == 0) {
  215.                 fseek(fp[1], head, 0);  /* re-do from head */
  216.                 nfld[1] = get2(1);      /* don't check key order */
  217.         } else
  218.                 head = p;       /* mark here */
  219.   }
  220. }
  221.  
  222. void f1_only()
  223. /* [<][>][^][v][top][bottom][index][help] */
  224. {
  225.   if ((aflag & F1) || (vflag & F1)) (*outfun) (F1);
  226.   nfld[0] = get1();
  227. }
  228.  
  229. void f2_only()
  230. /* [<][>][^][v][top][bottom][index][help] */
  231. {
  232.   if ((aflag & F2) || (vflag & F2)) (*outfun) (F2);
  233.   head = ftell(fp[1]);          /* mark */
  234.   nfld[1] = get2(0);            /* check key order */
  235. }
  236.  
  237. void output(f)
  238. /* [<][>][^][v][top][bottom][index][help] */
  239. {                               /* default output form */
  240.   if (f & F1)
  241.         fputs(fld[0][kpos[0]], stdout);
  242.   else
  243.         fputs(fld[1][kpos[1]], stdout);
  244.   if (f & F1) outfld(0);
  245.   if (f & F2) outfld(1);
  246.   fputc('\n', stdout);
  247. }
  248.  
  249. void outfld(file)
  250. /* [<][>][^][v][top][bottom][index][help] */
  251. {                               /* output all fields except key_field */
  252.   register int i;
  253.   int k, n;
  254.  
  255.   k = kpos[file];
  256.   n = nfld[file];
  257.   for (i = 0; i < n; i++)
  258.         if (i != k) {
  259.                 fputc(SEP, stdout);
  260.                 fputs(fld[file][i], stdout);
  261.         }
  262. }
  263.  
  264. void outputf(f)
  265. /* [<][>][^][v][top][bottom][index][help] */
  266. {                               /* output by '-o list' */
  267.   int i, j, k;
  268.   register char *s;
  269.  
  270.   for (i = k = 0; i < nout; i++) {
  271.         j = olist[i].o_file;
  272.         if ((f & (j + 1)) && (olist[i].o_field < nfld[j]))
  273.                 s = fld[j][olist[i].o_field];
  274.         else
  275.                 s = es;
  276.         if (s) {
  277.                 if (k++) fputc(SEP, stdout);
  278.                 fputs(s, stdout);
  279.         }
  280.   }
  281.   fputc('\n', stdout);
  282. }
  283.  
  284. int get1()
  285. /* [<][>][^][v][top][bottom][index][help] */
  286. {                               /* read file1 */
  287.   int r;
  288.   static char oldkey1[BUFSIZ];
  289.  
  290.   if (fld[0][kpos[0]]) {
  291.         strcpy(oldkey, fld[0][kpos[0]]);  /* save previous key for control */
  292.   }
  293.   r = getrec(0);
  294.  
  295.   if (r) {
  296.         if (strcmp(oldkey1, fld[0][kpos[0]]) > 0)
  297.               error("file1 is not sorted", (char *) 0);
  298.         strcpy(oldkey1, fld[0][kpos[0]]);  /* save prev key for sort check */
  299.   }
  300.   return r;
  301. }
  302.  
  303. int get2(back)
  304. /* [<][>][^][v][top][bottom][index][help] */
  305. {                               /* read file2 */
  306.   static char oldkey2[BUFSIZ];
  307.   int r;
  308.  
  309.   r = getrec(1);
  310.  
  311.   if (r) {
  312.         if (!back && strcmp(oldkey2, fld[1][kpos[1]]) > 0)
  313.               error("file2 is not sorted", (char *) 0);
  314.         strcpy(oldkey2, fld[1][kpos[1]]);  /* save prev key for sort check */
  315.   }
  316.   return r;
  317. }
  318.  
  319. int getrec(file)
  320. /* [<][>][^][v][top][bottom][index][help] */
  321. {                               /* read one line to split it */
  322.   if (fgets(buf[file], BUFSIZ, fp[file]) == (char *) 0)
  323.         *buf[file] = '\0';
  324.   else if (*buf[file] == '\n' || *buf[file] == '\r')
  325.         error("null line in file%s", file ? "1" : "0");
  326.  
  327.   return split(file);
  328. }
  329.  
  330. int split(file)
  331. /* [<][>][^][v][top][bottom][index][help] */
  332. {                               /* setup fields */
  333.   register int n;
  334.   register char *s, *t;
  335.  
  336.   for (n = 0, s = buf[file]; *s && *s != '\n' && *s != '\r';) {
  337.         if (sep) {
  338.                 for (t = s; *s && *s != sep && *s != '\n' && *s != '\r'; s++);
  339.         } else {
  340.                 while (*s == ' ' || *s == '\t')
  341.                         s++;    /* skip leading white space */
  342.                 for (t = s; *s && *s != ' ' && *s != '\t'
  343.                      && *s != '\n' && *s != '\r'; s++);
  344.                 /* We will treat trailing white space as NULL field */
  345.         }
  346.         if (*s) *s++ = '\0';
  347.         fld[file][n++] = t;
  348.         if (n == MAXFLD) error("too many filed in file%s", file ? "1" : "0");
  349.   }
  350.   fld[file][n] = (char *) 0;
  351.  
  352.   return n;
  353. }
  354.  
  355. FILE *efopen(file, mode)
  356. /* [<][>][^][v][top][bottom][index][help] */
  357. char *file, *mode;
  358. {
  359.   FILE *fp;
  360.  
  361.   if ((fp = fopen(file, mode)) == (FILE *) 0) error("can't open %s", file);
  362.  
  363.   return fp;
  364. }
  365.  
  366. void error(s, t)
  367. /* [<][>][^][v][top][bottom][index][help] */
  368. char *s, *t;
  369. {
  370.   fprintf(stderr, "%s: ", cmd);
  371.   fprintf(stderr, s, t);
  372.   fprintf(stderr, "\n");
  373.  
  374.   exit(1);
  375. }
  376. /* [<][>][^][v][top][bottom][index][help] */
  377.